/*
 * This file has definitions for some important file table
 * structures etc.
 */

#ifndef _FS_H
#define _FS_H

#include <sys/types.h>

/* devices are as follows: (same as minix, so we can use the minix
 * file system. These are major numbers.)
 *
 * 0 - unused (nodev)
 * 1 - /dev/mem
 * 2 - /dev/fd
 * 3 - /dev/hd
 * 4 - /dev/ttyx
 * 5 - /dev/tty
 * 6 - /dev/lp
 * 7 - unnamed pipes
 */

#define IS_SEEKABLE(x) ((x)>=1 && (x)<=3)

#define READ 0
#define WRITE 1
#define READA 2		/* read-ahead - don't pause */
#define WRITEA 3	/* "write-ahead" - silly, but somewhat useful */

void buffer_init(long buffer_end);

// Linux0.11中设备命名比较繁琐：主设备号标识设备种类，次设备号标识某个设备
// 逻辑设备号 = 主设备号 * 256 + 次设备号
// 也即 dev_no = (major << 8) + minor，例如以十六进制数字标识为：0xMMmm

// 根据设备号获取主设备号（主设备号即对应设备的种类）
#define MAJOR(a) (((unsigned)(a))>>8)
// 根据设备号获取子设备号（子设备号即对应硬盘上各分区）
#define MINOR(a) ((a)&0xff)

#define NAME_LEN 14					/* 文件名字支持的最大长度值 */
#define ROOT_INO 1					/* 文件夹系统中设备根目录对应的i节点号 */

#define I_MAP_SLOTS 8				/* i节点位图槽数（i节点位图占用的磁盘逻辑块数） */
#define Z_MAP_SLOTS 8				/* 逻辑块（区段块）位图槽数（逻辑块位图占用的磁盘逻辑块数） */
#define SUPER_MAGIC 0x137F			/* 文件系统魔数 */

#define NR_OPEN 20  				/* 单进程最大打开文件句柄数 */
#define NR_INODE 32					/* 系统同时最多使用i节点个数 */
#define NR_FILE 64					/* 系统最多文件个数（文件数组项数）*/
#define NR_SUPER 8					/* 系统所含超级块个数（超级块数组项数） ，或支持的最大文件系统数 */
#define NR_HASH 307					/* 缓冲区 Hash 表数组项数值 */
#define NR_BUFFERS nr_buffers		/* 系统所含缓冲块个数。在buffer_init()中进行初始化，初始化后不再改变*/
#define BLOCK_SIZE 1024				/* 数据块(逻辑块)长度（字节值）*/
#define BLOCK_SIZE_BITS 10			/* 数据块长度所占比特位数 */
#ifndef NULL
#define NULL ((void *) 0)
#endif

#define INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct d_inode)))	/* 每个逻辑块可存放的i节点数 */
#define DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct dir_entry)))	/* 每个逻辑块可存放的目录项数 */

// 管道头指针
#define PIPE_HEAD(inode) ((inode).i_zone[0])
// 管道尾指针
#define PIPE_TAIL(inode) ((inode).i_zone[1])
// 管道大小
#define PIPE_SIZE(inode) ((PIPE_HEAD(inode)-PIPE_TAIL(inode))&(PAGE_SIZE-1))
// 管道空?
#define PIPE_EMPTY(inode) (PIPE_HEAD(inode)==PIPE_TAIL(inode))
// 管道满？
#define PIPE_FULL(inode) (PIPE_SIZE(inode)==(PAGE_SIZE-1))
// 管道头指针递增
#define INC_PIPE(head) \
__asm__("incl %0\n\tandl $4095,%0"::"m" (head))

typedef char buffer_block[BLOCK_SIZE];	// 块缓冲区（buffer_block是一个数据类型，表示一个连续1024个字符的数据结构）

// 缓冲块头数据结构（用于描述一个buffer的数据结构）
struct buffer_head {
	char * b_data;			/* pointer to data block (1024 bytes) 指向缓冲区中的一个缓冲区块（地址） 。在缓冲区初始化时已经全部设置完成*/
	unsigned long b_blocknr;	/* block number 缓冲块中的数据对应的磁盘块号（逻辑块号，不是逻辑块块号） */
	unsigned short b_dev;		/* device (0 = free) 缓冲块中的数据对应的块设备，分区设备号（逻辑设备号，例如 301、302） */
	// b_uptodate 标志用于表示缓冲块中的数据是与块设备上的同步，即在从块设备上读入缓冲块后没有修改过。
	unsigned char b_uptodate;   /* 0 - 表示与块设备不同步；1 - 表示与块设备同步，为最新的 (当数据被写入块设备或刚从块设备中读入缓冲块,则数据变成有效/可用)*/
	// b_dirt 标志用于表示缓冲块中的数据是否已经被修改过。
	unsigned char b_dirt;		/* 0-clean,1-dirty */
	unsigned char b_count;		/* users using this block 使用该buffer的用户数（进程数），为0时，表示该缓冲区块空闲 */
	unsigned char b_lock;		/* 0 - ok, 1 -locked 是否上锁，缓冲区块可能被多个进程使用，但不一定上锁 */
	struct task_struct * b_wait;		/* 指向等待该缓冲区解锁的任务 */
	struct buffer_head * b_prev;		/* hash 队列上前一块（这四个指针用于缓冲区的管理）*/
	struct buffer_head * b_next;
	struct buffer_head * b_prev_free;	/* 空闲表上前一块 */
	struct buffer_head * b_next_free;
};

// 磁盘上的索引节点(i节点)数据结构（disk_inode）
struct d_inode {
	unsigned short i_mode;		// 文件类型和属性(rwx 位）
	unsigned short i_uid;		// 用户 id（文件拥有者标识符）
	unsigned long i_size;		// 文件大小（字节数）
	unsigned long i_time;		// 修改时间（自1970.1.1:0算起，秒）
	unsigned char i_gid;		// 组 id(文件拥有者所在的组）
	unsigned char i_nlinks;		// 硬链接数（多少个文件目录项指向该i节点)
	unsigned short i_zone[9];	// 逻辑块数组，每个元素表示一个数据逻辑块。直接（0-6)、间接（7）或双重间接（8）逻辑块号
};

// 内存中的i节点结构。前7项与 d_inode 完全一样（memory_inode）
struct m_inode {
	unsigned short i_mode;			// 文件类型(_dspcb)和属性(rwx 位）
	unsigned short i_uid;			// 用户 id（文件拥有者标识符）
	unsigned long i_size;			// 文件大小（字节数）（对于目录：目录项长度）。对于管道，其表示管道文件的内存地址
	unsigned long i_mtime;			// 文件修改时间（自1970.1.1:0算起，秒，Modify time）
	unsigned char i_gid;			// 组 id(文件拥有者所在的组）
	unsigned char i_nlinks;			// 硬链接数（多少个文件目录项指向该i节点)
	unsigned short i_zone[9];		// 文件对应的逻辑块（数组），每个元素表示一个数据逻辑块块号。直接（0-6)、间接（7）或双重间接（8）逻辑块号
									// 如果是块设备文件：izone[O]中是设备号。
									// 如果是管道文件：i_zone[0]存储管道头内存地址；i_zone[1]存储管道尾内存地址
/* these are in memory also */
	struct task_struct * i_wait;	// 等待该i节点的进程
	unsigned long i_atime;			// 最后访问时间
	unsigned long i_ctime;			// i节点自身修改时间（Change time）
	unsigned short i_dev;			// i节点所存储在的设备号，0表示该inode未使用设备。例如管道文件的i_dev=0
	unsigned short i_num;			// i节点号
	unsigned short i_count;			// i节点被使用的次数，0 表示该i节点空闲
	unsigned char i_lock;			// 锁定标志
	unsigned char i_dirt;			// 已修改(脏）标志，表示内存中inode数据结构发生修改
	unsigned char i_pipe;			// 管道是否使用标志
	unsigned char i_mount;			// 安装标志（是否是文件系统的挂载点，是否有文件系统挂载到该inode上）
	unsigned char i_seek;			// 搜寻标志（1seek时）
	unsigned char i_update;			// 更新标志
};

// 文件结构（用于在文件句柄与i节点之间建立关系）
struct file {
	unsigned short f_mode;			// 文件操作模式（RW 位）
	unsigned short f_flags;			// 文件打开和控制的标志
	unsigned short f_count;			// 对应文件引用计数值
	struct m_inode * f_inode;		// 指向对应i节点
	off_t f_pos;					// 文件读写偏移值
};

// 内存中磁盘超级块结构
struct super_block {
	unsigned short s_ninodes;			// i节点数（文件系统中inode总数）
	unsigned short s_nzones;			// 设备上以逻辑块为单位的总逻辑块数（不仅仅是数据区），即设备逻辑块总数
	unsigned short s_imap_blocks;		// i节点位图所占用的数据块数
	unsigned short s_zmap_blocks;		// 逻辑块位图所占用的数据块数
	unsigned short s_firstdatazone;		// 数据区开始处第一个数据逻辑块（对应的磁盘块）块号（第1个数据块对应的盘块号）
	unsigned short s_log_zone_size;		// log2X。（以2为底）。表示每个逻辑块包含几个磁盘块
	unsigned long s_max_size;			// 文件最大长度（单位：字节）
	unsigned short s_magic;				// 文件系统魔数
/* These are only in memory */
	struct buffer_head * s_imap[8];		// i节点位图缓冲块指针数组（占用8块，可表示 64M）
	struct buffer_head * s_zmap[8];		// 逻辑块位图所在缓冲块指针数组（占用8块）
	unsigned short s_dev;				// 超级块所在设备号（该超级块对应的设备号）。s_dev=0 的项表示为空项
	struct m_inode * s_isup;			// 被安装的文件系统的 根目录的i节点 。(isup - super inode。) 文件系统的根目录"/"的inode
	struct m_inode * s_imount;			// 文件系统被安装 到 的i节点(超级块所指明的被安装到的i节点) 挂载点的inode
	unsigned long s_time;				// 修改时间
	struct task_struct * s_wait;		// 等待该超级块的进程
	unsigned char s_lock;				// 被锁定标志
	unsigned char s_rd_only;			// 只读标志
	unsigned char s_dirt;				// 已修改(脏）标志
};

// 磁盘上超级块结构（与内存中磁盘超级块结构super_block的前半部分完全一致）
struct d_super_block {
	unsigned short s_ninodes;			// i节点数
	unsigned short s_nzones;			// 逻辑块数
	unsigned short s_imap_blocks;		// i节点位图所占用的数据块数
	unsigned short s_zmap_blocks;		// 逻辑块位图所占用的数据块数
	unsigned short s_firstdatazone;		// 第一个数据逻辑块号
	unsigned short s_log_zone_size;		// log(数据块数/逻辑块）。（以2为底）
	unsigned long s_max_size;			// 文件最大长度
	unsigned short s_magic;				// 文件系统魔数
};

// 文件目录项结构
struct dir_entry {
	unsigned short inode;		// inode号
	char name[NAME_LEN];		// 文件（或目录）名
};

extern struct m_inode inode_table[NR_INODE];		// 定义i节点表（32 项）
extern struct file file_table[NR_FILE];				// 文件表数组 （64 项）
extern struct super_block super_block[NR_SUPER];	// 超级块数组（8项）
extern struct buffer_head * start_buffer;			// 缓冲区起始内存位置
extern int nr_buffers;								// 缓冲块数

extern void check_disk_change(int dev);
extern int floppy_change(unsigned int nr);
extern int ticks_to_floppy_on(unsigned int dev);
extern void floppy_on(unsigned int dev);
extern void floppy_off(unsigned int dev);
extern void truncate(struct m_inode * inode);
extern void sync_inodes(void);
extern void wait_on(struct m_inode * inode);
extern int bmap(struct m_inode * inode,int block);
extern int create_block(struct m_inode * inode,int block);
extern struct m_inode * namei(const char * pathname);
extern int open_namei(const char * pathname, int flag, int mode,
	struct m_inode ** res_inode);
extern void iput(struct m_inode * inode);
extern struct m_inode * iget(int dev,int nr);
extern struct m_inode * get_empty_inode(void);
extern struct m_inode * get_pipe_inode(void);
extern struct buffer_head * get_hash_table(int dev, int block);
extern struct buffer_head * getblk(int dev, int block);
extern void ll_rw_block(int rw, struct buffer_head * bh);
extern void brelse(struct buffer_head * buf);
extern struct buffer_head * bread(int dev,int block);
extern void bread_page(unsigned long addr,int dev,int b[4]);
extern struct buffer_head * breada(int dev,int block,...);
extern int new_block(int dev);
extern void free_block(int dev, int block);
extern struct m_inode * new_inode(int dev);
extern void free_inode(struct m_inode * inode);
extern int sync_dev(int dev);
extern struct super_block * get_super(int dev);
extern int ROOT_DEV;  /* 根文件系统所在的设备分区 */

extern void mount_root(void);

#endif
